package FileStructure;

import Tools.cons;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

public class FileHandler {

    public String fileName;
    public String torrentName;
    public String failure_reason_value;
    public String warning_reason_value;
    public String interval_value;
    public String complete_value;
    public String incomplete_value;
    public long fileLength;
    public final int pieceLength = cons.pieceLength;
    public String announce;
    public ArrayList<String> piecesHashVal;
    public ArrayList<String> ip_peer;
    public ArrayList<String> port_peer;

    public FileHandler() {
        this.fileName = "";
        this.torrentName = "";
        this.failure_reason_value = "";
        this.warning_reason_value = "";
        this.interval_value = "";
        this.complete_value = "";
        this.incomplete_value = "";
        this.announce = "";
        this.piecesHashVal = new ArrayList<String>();
        this.ip_peer = new ArrayList<String>();
        this.port_peer = new ArrayList<String>();
    }

    public static String convertToSHA1(byte[] input) {
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance("SHA-1");
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(FileHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
        md.update(input);
        byte[] sha1hash = md.digest();

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < sha1hash.length; i++) {
            sb.append(Integer.toString((sha1hash[i] & 0xff) + 0x100, 16).substring(1));
        }
        return sb.toString();
    }

    public void hashFile(String dir, String namafile) throws IOException {
        File f = new File(dir + namafile);
        FileInputStream fis = new FileInputStream(f);
        int numPieces = (int) (f.length() / pieceLength) + ((f.length()%pieceLength)==0 ? 0 : 1);
        piecesHashVal.clear();
        byte[] buffer;
        for (int i=0; i<numPieces; i++) {
            if (i == numPieces - 1) {
                // Penanganan untuk piece terakhir (panjang buffer tidak selalu sama dengan pieceLength)
                buffer = new byte[(int) (f.length() - (i*pieceLength))];
                fis.read(buffer);
            } else {
                buffer = new byte[cons.pieceLength];
                fis.read(buffer, 0, cons.pieceLength);
            }
            //fis.read(buffer, 0, pieceLength);
            piecesHashVal.add(convertToSHA1(buffer));
        }
    }

    public void createTorrent(String sharedDir, String sharedFileName, String torrentDir, String announceURL) throws IOException {
        hashFile(sharedDir, sharedFileName);
        File sharedFile = new File(sharedDir + sharedFileName);
        if (sharedFile.exists() && sharedFile.isFile()) {
            //fileName = sharedFile.getName().replace(' ', '_');
            fileName = sharedFile.getName();
            torrentName = fileName + ".torrent";
            announce = announceURL;
            try {
                File torrentFile = new File(torrentDir + torrentName);
                FileOutputStream fos = new FileOutputStream(torrentFile);
                PrintStream writer = new PrintStream(fos);
                fileLength = sharedFile.length();
                writer.println(fileName + " " + fileLength + " " + pieceLength);
                writer.println(announce);
                for (int i = 0; i < piecesHashVal.size(); i++) {
                    writer.println(piecesHashVal.get(i));
                }
                fos.close();
            } catch (IOException e) {
            }
        } else {
            System.out.println("File not found.");
        }
    }

    public void readTorrent(String dir, String file_name) throws FileNotFoundException {
        File torrFile = new File(dir + file_name);
        if (torrFile.canRead()) {
            java.util.Scanner baca = new java.util.Scanner(torrFile);
            // Baca Header file
            String textValue[] = baca.nextLine().split(" ");
            this.torrentName = textValue[0] + ".torrent";
            fileName = textValue[0];
            fileLength = Long.parseLong(textValue[1]);
            // Baca Announce URL
            announce = baca.nextLine();
            piecesHashVal.clear();
            while (baca.hasNextLine()) {
                piecesHashVal.add(baca.nextLine());
            }
        }
    }

    public void readResponseFile(String dir) throws FileNotFoundException {
        File respFile = new File(dir + "response.txt");
        if (respFile.canRead()) {
            java.util.Scanner baca = new java.util.Scanner(respFile);
            String text[] = baca.nextLine().split(" ");
            if (text.length == 2) {
                failure_reason_value = text[1];
            }
            //System.out.println(text[0] + text[1]);
            String text2[] = baca.nextLine().split(" ");
            for (int i = 1; i < text2.length; i++) {
                warning_reason_value += text2[i] + " ";
            }
            //warning_reason_value = text2[1];
            interval_value = baca.nextLine().split(" ")[1];
            complete_value = baca.nextLine().split(" ")[1];
            incomplete_value = baca.nextLine().split(" ")[1];
            ip_peer.clear();
            port_peer.clear();
            String parse1[] = baca.nextLine().split(" ");
            for (int i = 1; i < parse1.length; i++) {
                String parse2[] = parse1[i].split(":");
                ip_peer.add(parse2[0]);
                port_peer.add(parse2[1]);
            }
        }
    }

    public long getFileSize(String filename) {
        File file = new File(filename);
        if (!file.exists() || !file.isFile()) {
            System.out.println("File doesn\'t exist");
            return -1;
        }
        //Here we get the actual size
        return file.length() / pieceLength;
    }

    public static byte[] generatePayLoad(String dir, String filename, int index) throws IOException {
        // Input: dir, filename, index
        // Output: begin, block
        File f = new File(dir + filename);
        FileInputStream fis = new FileInputStream(f);
        int numPieces = (int) (f.length() / cons.pieceLength) + ((f.length()%cons.pieceLength)==0 ? 0 : 1);
        int begin = index*cons.pieceLength;
        fis.skip(begin);
        byte[] block;
        if (index == numPieces-1) {
            // Penanganan untuk piece terakhir (panjang buffer tidak selalu sama dengan pieceLength)
            block = new byte[(int) (f.length() - begin)];
            fis.read(block);
        } else {
            block = new byte[cons.pieceLength];
            fis.read(block, 0, cons.pieceLength);
        }
        return block;
    }

    public static void writePayloadToFile(File f, int index, byte[] payload){
        FileOutputStream fos = null;
        try {
            //File f = new File(dir + out_filename);
            fos = new FileOutputStream(f);
            fos.write(payload, index, cons.pieceLength);
        } catch (IOException ex) {
            Logger.getLogger(FileHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
        finally {
            try {
                fos.close();
            } catch (IOException ex) {
                Logger.getLogger(FileHandler.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }

    public static void main(String[] args) throws Exception {
        FileHandler fh = new FileHandler();
        String dir = "test/";
		String sharedFile = "laguku.mp3";
		String torrentFile = "laguku.mp3.torrent";
        fh.createTorrent(dir, sharedFile, "test/", cons.announceURL);
		fh.readTorrent(dir, torrentFile);
        System.out.println("fileName = " + fh.fileName);
        System.out.println("fileLength = " + fh.fileLength);
        System.out.println("announce = " + fh.announce);
        System.out.println("piecesHashVal:");
        for (String entry : fh.piecesHashVal) {
            System.out.println(entry);
        }

        int index = 14;
        byte[] block = FileHandler.generatePayLoad(dir, sharedFile, index);
        System.out.println("Harusnya nilai hash ini sama dengan nilai hash piece ke-" + index + " dari file");
        System.out.println(block.length);
        System.out.println(FileHandler.convertToSHA1(block));

        index = 15;
        byte[] block2 = FileHandler.generatePayLoad(dir, sharedFile, index);
        System.out.println("Harusnya nilai hash ini sama dengan nilai hash piece ke-" + index + " dari file");
        System.out.println(block2.length);
        System.out.println(FileHandler.convertToSHA1(block2));
    }
}
